package com.piece.core.verifycode.processor;

import com.piece.core.framework.constant.ExceptionAuthConstants;
import com.piece.core.framework.constant.ParamConstants;
import com.piece.core.framework.util.basic.I18nUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.framework.util.cache.CacheFactory;
import com.piece.core.verifycode.enums.ValidateCodeType;
import com.piece.core.verifycode.exception.ValidateCodeException;
import com.piece.core.verifycode.factory.ValidateCode;
import com.piece.core.verifycode.factory.ValidateCodeGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * 验证码处理器抽象类
 */
public abstract class AbstractValidateCodeProcessor<C extends ValidateCode> implements ValidateCodeProcessor {

    /**
     * 收集系统中所有的{@link ValidateCodeGenerator}接口的实现
     */
    @Autowired
    private Map<String, ValidateCodeGenerator> validateCodeGenerators;

    @Autowired
    private CacheFactory cacheFactory;

    @Override
    public void create(HttpServletRequest request, HttpServletResponse response) throws Exception {
        C validateCode = generate(request, response);
        save(request.getParameter(ParamConstants.VALIDATE_CODE_SESSION), validateCode);
        send(request, response, validateCode);
    }

    /**
     * 生成校验码
     */
    private C generate(HttpServletRequest request, HttpServletResponse response) {
        String type = getValidateCodeType().name().toLowerCase();
        String generatorName = type + ValidateCodeGenerator.class.getSimpleName();
        ValidateCodeGenerator validateCodeGenerator = validateCodeGenerators.get(generatorName);
        if (null == validateCodeGenerator) {
            throw new ValidateCodeException(I18nUtil.message(ExceptionAuthConstants.VALIDATE_CODE_UNKNOW, generatorName));
        }
        return (C) validateCodeGenerator.generate(request, response);
    }

    /**
     * 获取校验码的类型
     */
    private ValidateCodeType getValidateCodeType() {
        String type = StringUtil.substringBefore(getClass().getSimpleName(), "ValidateCodeProcessor");
        return ValidateCodeType.valueOf(type.toUpperCase());
    }

    /**
     * 保存校验码
     */
    private void save(String token, C validateCode) {
        ValidateCode code = new ValidateCode(validateCode.getMathResult(), validateCode.getExpire());
        cacheFactory.get().setExpire(getSessionKey(token), code, code.getExpire());
    }

    /**
     * 验证码放入session时的key
     */
    private String getSessionKey(String token) {
        return SESSION_KEY_PREFIX + getValidateCodeType().toString().toUpperCase() + ":" + token;
    }

    /**
     * 发送校验码，由子类实现
     */
    public abstract void send(HttpServletRequest request, HttpServletResponse response, C validateCode) throws Exception;

    @Override
    public void validate(String token, String codeInRequest) {
        ValidateCode codeInCache = cacheFactory.get().get(getSessionKey(token), ValidateCode.class);

        if (StringUtil.isBlank(codeInRequest)) {
            throw new ValidateCodeException();
        }

        if (null == codeInCache) {
            throw new ValidateCodeException(I18nUtil.message(ExceptionAuthConstants.VALIDATE_CODE_EXPIRED));
        }

        if (LocalDateTime.now().isAfter(codeInCache.getExpireTime())) {
            cacheFactory.get().delete(getSessionKey(token));
            throw new ValidateCodeException(I18nUtil.message(ExceptionAuthConstants.VALIDATE_CODE_EXPIRED));
        }

        if (!StringUtil.equals(codeInCache.getCode(), codeInRequest)) {
            throw new ValidateCodeException();
        }

        cacheFactory.get().delete(getSessionKey(token));
    }

    @Override
    public String getSessionKeyPrefix() {
        return SESSION_KEY_PREFIX + getValidateCodeType().toString().toUpperCase() + ":";
    }
}
